﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace Zhp.Common.Extention
{
    public static class ListExt
    {
        public static List<T> ToTree<T>(this List<T> list, Expression<Func<T, object>> pkIdExpression, Expression<Func<T, IEnumerable<object>>> childListExpression, Expression<Func<T, object>> parentIdExpression, object rootValue)
        {
            string pkIdName = GetParentName(pkIdExpression);
            var childName = ((childListExpression as LambdaExpression).Body as MemberExpression).Member.Name;
            string parentIdName = GetParentName(parentIdExpression);

            return BuildTree(list, pkIdName, parentIdName, childName, rootValue)?.ToList() ?? default;
        }

        private static string GetParentName<T>(Expression<Func<T, object>> parentIdExpression)
        {
            var exp = (parentIdExpression as LambdaExpression).Body;
            if (exp is UnaryExpression)
            {
                exp = (exp as UnaryExpression).Operand;
            }
            var parentIdName = (exp as MemberExpression).Member.Name;
            return parentIdName;
        }

        private static IEnumerable<T> BuildTree<T>(IEnumerable<T> list, string idName, string pIdName, string childName, object rootValue)
        {
            var type = typeof(T);
            var mainIdProp = type.GetProperty(idName);
            var pIdProp = type.GetProperty(pIdName);
            var childProp = type.GetProperty(childName);

            var kvList = list.ToDictionary(x => mainIdProp.GetValue(x).ObjToString());
            var group = list.GroupBy(x => pIdProp.GetValue(x).ObjToString());

            var root = rootValue != null ? group.FirstOrDefault(x => x.Key == rootValue.ObjToString()) : group.FirstOrDefault(x => x.Key == null || x.Key == "" || x.Key == "0" || x.Key == Guid.Empty.ToString());

            if (root != null)
            {
                foreach (var item in group)
                {
                    if (kvList.TryGetValue(item.Key, out var parent))
                    {
                        childProp.SetValue(parent, item.ToList());
                    }
                }
            }

            return root;
        }

        public static List<T> ToList<T>(this List<T> treeList, Expression<Func<T, IEnumerable<object>>> childListExpression)
        {
            var childName = ((childListExpression as LambdaExpression).Body as MemberExpression).Member.Name;

            return BuildList(treeList, childName);
        }

        private static List<T> BuildList<T>(List<T> treeList, string childName)
        {
            var type = typeof(T);
            var childProp = type.GetProperty(childName);

            var list = new List<T>();

            TreeToList(list, treeList, childProp);

            return list;
        }

        private static void TreeToList<T>(List<T> list, List<T> treeList, PropertyInfo childProp)
        {
            foreach (var item in treeList)
            {
                var childValue = childProp.GetValue(item, null);

                childProp.SetValue(item, null);
                list.Add(item);

                if (childValue != null)
                {
                    var childList = (List<T>)childValue;
                    if (childList.Any())
                    {
                        TreeToList(list, childList, childProp);
                    }
                }
            }
        }

        public static string ObjToString(this object thisValue)
        {
            if (thisValue != null) return thisValue.ToString().Trim();
            return "";
        }
    }
}
